'''
Created on 2010/07/06

@author: yukiyonezawa
'''
from pyrfid.DataProtocol.DSFID.AccessMethod.AccessMethod import AccessMethod
from pyrfid.DataProtocol.DSFID.DataFormat.DataFormatUtil import DataFormatUtil
from pyrfid.Util.Util import Util
from pyrfid.DataProtocol.DSFID.Processor.Compactor import Compactor

import sys
from pyrfid.DataProtocol.DSFID.AccessMethod.NonDirectory import NonDirectory
from pyrfid.DataProtocol.LogicalMemory.DataSet import DataSet
from pyrfid.DataProtocol.CommandResponseUnit.ReadObjectsCRU import ReadObjectsCRU
from pyrfid.DataProtocol.DSFID.DataFormat.DataFormat import DataFormat
from pyrfid.DataProtocol.AddObject import AddObject

class Directory(AccessMethod):
    '''
    classdocs
    '''
    ACCESS_METHOD_ID = 1
    driver = object()
    
    ALLOW_COMPACTION_TYPE = [AddObject.APPLICATION_DEFINED, AddObject.COMPACT, AddObject.UTF8_DATA]
    
    def __init__(self):
        '''
        Constructor
        '''
        pass
    def setDriver(self, driver):
        self.driver = driver
    
    def readAndAnalyzeLogicalMemoryMap(self, tag_id, highest_address, data_format):
        '''
        start_address -- a int data
        data_format -- Data Format
        '''
        
        index = highest_address - 1
        end_address_of_data_set = ""
        
        data_set_list = list()
        while True:
            precursor = self.driver.readUserMemory(tag_id, index, 1)
            if precursor == "00":
                break
            
            data_set = DataSet()
            data_set.PRECURSOR = Util.convertHexToBit(precursor)
            
            length_of_object_identifier_type = ""
            length_of_object_identifier = ""
            
            if DataFormatUtil.isPrecursorHaveAllObjectIdentifier(precursor) == False:
                index -= 1
                length_of_object_identifier_type = self.driver.readUserMemory(tag_id, index, 1)
                while DataFormatUtil.isLengthOfObjectIdentifier(length_of_object_identifier_type) == False:
                    index -= 1
                    length_of_object_identifier_type += self.driver.readUserMemory(tag_id, index, 1)            
                
                data_set.LENGTH_OF_OBJECT_IDENTIFIER = length_of_object_identifier_type
                
                length_of_object_identifier = data_format.decodeLengthOfObjectIdentifierType(length_of_object_identifier_type)
                index -= length_of_object_identifier
                object_identifier = self.driver.readUserMemory(tag_id, index, length_of_object_identifier)
                data_set.OBJECT_IDENTIFIER = object_identifier
            
            data_set_list.append(data_set)
            index -= 1
            end_address_of_data_set = self.driver.readUserMemory(tag_id, index, 1)
            index -= 1
            
        return data_set_list
    
    def encodeDirectory(self, start_address, formatted_add_data_set_list):
        encoded_directory = list()
        write_start_address = start_address
        for data_set in formatted_add_data_set_list:
            encoded_directory.append(Util.convertBitToHex(data_set.PRECURSOR))
            encoded_directory.append(data_set.LENGTH_OF_OBJECT_IDENTIFIER)
            encoded_directory.append(data_set.OBJECT_IDENTIFIER)
            encoded_directory.append( Util.convertIntToHex(write_start_address, 2) )
            write_start_address += len(data_set.getLogicalMemory()) / 2
        encoded_directory.reverse()
        directory_structure = ""
        for data in encoded_directory:
            directory_structure += data
        
        return directory_structure
    
    def addData(self, tag_id, dsfid_on_tag, data_format, add_objects_list):
        
        highest_address = self.driver.getUserMemoryLength(tag_id)
        
        existing_directory_data_set_list = []
        existing_data_set_list = []
        if dsfid_on_tag != "":
            existing_directory_data_set_list = self.readAndAnalyzeLogicalMemoryMap(tag_id, highest_address, data_format)
        
            non_directory_access_method = NonDirectory()
            non_directory_access_method.setDriver(self.driver)
            existing_data_set_list = non_directory_access_method.readAndAnalyzeLogicalMemoryMap(tag_id, 0, data_format)
        
            if len(existing_directory_data_set_list) != len(existing_data_set_list):
                raise "MISS MATCHED"
        
        add_data_set_list = DataFormatUtil.convertDataSet(add_objects_list)
        
        ########################################################################
        #reference 15962 Annex G5
        #formatting
        formatted_add_data_set_list = data_format.formattingDataSet(existing_data_set_list, add_data_set_list)
        
        directory_structure = self.encodeDirectory(0, formatted_add_data_set_list)
        logical_memory_map = ""
        for data_set in formatted_add_data_set_list:
            logical_memory_map += data_set.getLogicalMemory()
        
        if len(logical_memory_map) % 2 == 1:
            logical_memory_map += "0"
                
        logical_memory_map += (highest_address - len(logical_memory_map) / 2) * "00"
        start_address_of_directory_structure = len(logical_memory_map) - len(directory_structure)
        
        logical_memory_map = logical_memory_map[:start_address_of_directory_structure] + directory_structure
        
        self.driver.writeUserMemory(tag_id, 0, logical_memory_map)
        
    
    def readData(self, tag_id, data_format, read_type):
        
        highest_address = self.driver.getUserMemoryLength(tag_id)        
        existing_data_set_list = []
        if read_type == ReadObjectsCRU.READ_ALL_OBJECTS:
            non_directory_access_method = NonDirectory()
            non_directory_access_method.setDriver(self.driver)
            existing_data_set_list = non_directory_access_method.readAndAnalyzeLogicalMemoryMap(tag_id, 0, data_format)
            
        else:
            pass
        add_object_list = data_format.decodeDataSetToAddData(existing_data_set_list)        
        return add_object_list